package com.mlog.njyjy.ndop.process.admin.service.impl;

import cn.hutool.core.util.StrUtil;
import com.mlog.njyjy.ndop.common.domain.*;
import com.mlog.njyjy.ndop.common.support.DateTimeUtil;
import com.mlog.njyjy.ndop.persistence.dao.*;
import com.mlog.njyjy.ndop.process.admin.service.NodeService;
import com.mlog.njyjy.ndop.process.admin.support.ExcelParseUtils;
import com.mlog.njyjy.ndop.process.admin.support.JxlsUtils;
import com.mlog.njyjy.ndop.process.admin.support.enums.CollectWay;
import com.mlog.njyjy.ndop.share.sql.support.SqlTableUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.stream.Collectors;

import static com.mlog.njyjy.ndop.process.admin.support.JxlsUtils.TPL_PATH;

/**
 * <p>
 *
 * </p>
 *
 * @author zyz
 * @version v1.0, 2020/8/19
 */
@Slf4j
@Service
public class NodeServiceImpl implements NodeService {

    private final NodeRepository repository;

    private final MessageCollectItemRepository messageCollectItemRepository;

    private final FtpCollectItemRepository ftpCollectItemRepository;

    private final ApiCollectItemRepository apiCollectItemRepository;

    private final BizItemRepository bizItemRepository;

    private final ContactPersonRepository contactPersonRepository;

    private static final int START_COL_INDEX = 2;

    public NodeServiceImpl(NodeRepository repository, MessageCollectItemRepository messageCollectItemRepository,
                           FtpCollectItemRepository ftpCollectItemRepository, ApiCollectItemRepository apiCollectItemRepository,
                           BizItemRepository bizItemRepository, ContactPersonRepository contactPersonRepository) {
        this.repository = repository;
        this.messageCollectItemRepository = messageCollectItemRepository;
        this.ftpCollectItemRepository = ftpCollectItemRepository;
        this.apiCollectItemRepository = apiCollectItemRepository;
        this.bizItemRepository = bizItemRepository;
        this.contactPersonRepository = contactPersonRepository;
    }

    /**
     * 列出指定节点组的所有节点
     *
     * @param groupId
     * @return
     */
    @Override
    public List<Node> listByGroup(Long groupId) {
        return repository.findByField("group_id", groupId);
    }

    /**
     * 保存节点 新增或者修改
     *
     * @param nodeDto
     * @return
     */
    @Override
    public boolean save(Node nodeDto) {
        Long id = nodeDto.getId();
        boolean succeed;
        if (Objects.isNull(id)) {
            //insert
            succeed = repository.insert(nodeDto);
        } else {
            //update
            succeed = repository.update(nodeDto);
        }
        if (succeed) {
            Node existed = repository.findFirst("select * from " + SqlTableUtil.getTblName(Node.class) + " where name = '" + nodeDto.getName()
                    + "' and group_id = " + nodeDto.getGroupId());
            if (existed != null) {
                //1.保存采集方式信息
                saveCollectWay(nodeDto, existed.getId());

                //2. 保存业务参数
                saveBizItem(nodeDto, existed.getId());

            }
        }
        return succeed;
    }


    /**
     * 删除单个节点
     *
     * @param id
     * @return
     */
    @Override
    public boolean delete(Long id) {
        Node node = repository.findOne(id);
        if (node != null) {
            boolean succeed = repository.delete(id);
            if (succeed) {
                //删除collectWayItem
                deleteCollectWay(node);

                //删除bizItem
                deleteBizItem(node.getId());
            }
            return succeed;
        }

        return false;
    }


    /**
     * 批量删除节点
     *
     * @param ids
     * @return
     */
    @Override
    public boolean deleteBatch(String ids) {
        String[] idArr = ids.split(",");
        boolean flag = true;
        for (String id : idArr) {
            Long nodeId = Long.parseLong(id);
            boolean succeed = delete(nodeId);
            if (!succeed) {
                flag = false;
            }
        }
        return flag;
    }

    /**
     * 导出节点配置文件
     *
     * @return
     */
    @Override
    public byte[] exportFile(Long groupId) {
        Map<String, Object> model = new HashMap<>();
        List<Node> nodeList;
        if (Objects.isNull(groupId)) {
            nodeList = repository.findAll();
        } else {
            nodeList = listByGroup(groupId);
        }
        Map<Long, List<Node>> groupMap = nodeList.stream().collect(Collectors.groupingBy(Node::getGroupId));
        List<Node> itemList = new ArrayList<>();
        groupMap.forEach((key, value) -> {
            List<Node> items = value.stream().sorted(Comparator.comparingInt(Node::getSequence)).collect(Collectors.toList());
            itemList.addAll(items);
        });
        model.put("nodeList", itemList);
        InputStream tplStream = NodeServiceImpl.class.getClassLoader().getResourceAsStream(TPL_PATH);
        return JxlsUtils.exportExcel(tplStream, model);
    }

    /**
     * 导入节点配置文件
     *
     * @return
     */
    @Override
    public boolean importFile(MultipartFile file, Long groupId) throws IOException {

        List<String[]> excelDataList = ExcelParseUtils.getExcelData(file);

        boolean flag = true;
        if (excelDataList.size() > 0) {
            //先按节点次序排序 节点名称分组
            Map<String, List<String[]>> map = excelDataList.stream().sorted(Comparator.comparing(strings -> strings[START_COL_INDEX]))
                    .collect(Collectors.groupingBy(strings -> strings[0]));
            int length = excelDataList.get(0).length;

            for (String key : map.keySet()) {
                try {
                    List<String[]> value = map.get(key);

                    String[] first = value.get(0);
                    Node node = new Node().setName(key).setGroupId(groupId)
                            .setSequence((int) Double.parseDouble(first[START_COL_INDEX]))
                            .setCollectWay(first[START_COL_INDEX + 2]);
                    //获取系统负责人信息
                    String contactPersonName = first[START_COL_INDEX + 1];
                    ContactPerson contactPerson = contactPersonRepository.findByName(contactPersonName);
                    if (contactPerson != null) {
                        node.setContactPersonId(contactPerson.getId());
                    }
                    Node existCheck = repository.findByNameAndGroup(key, groupId);
                    boolean succeed;
                    if (existCheck != null) {
                        node.setId(existCheck.getId());
                        succeed = repository.update(node);
                    } else {
                        succeed = repository.insert(node);
                    }

                    if (succeed) {
                        Node existed = repository.findByNameAndGroup(key, groupId);
                        if (existed != null) {
                            Long nodeId = existed.getId();

                            //处理消息配置
                            saveCollectWay(existed, first);

                            //处理业务配置
                            saveBizItems(nodeId, length, value);
                        }
                    } else {
                        flag = false;
                    }
                } catch (Exception e) {
                    log.error(e.getLocalizedMessage());
                }
            }
        } else {
            flag = false;
        }
        return flag;
    }

    /**
     * 更换组
     *
     * @param groupId
     * @param nodeIds
     * @return
     */
    @Override
    public boolean changeGroup(Long groupId, String nodeIds) {
        if (StrUtil.isBlank(nodeIds)) {
            return false;
        } else {
            String[] ids = nodeIds.split(",");
            boolean flag = true;
            for (String id : ids) {
                Long nodeId = Long.parseLong(id);
                Node node = repository.findOne(nodeId);
                if (node != null) {
                    node.setGroupId(groupId);
                    Node exist = repository.findByNameAndGroup(node.getName(), groupId);
                    if (exist != null) {
                        log.error("node of name {} in nodeGroup {} has been existed", node.getName(), groupId);
                        flag = false;
                    } else {
                        boolean succeed = repository.update(node);
                        if (!succeed) {
                            flag = false;
                        }
                    }
                }
            }
            return flag;
        }
    }

    @Override
    public boolean copyNode(Long nodeId, String name) {
        Node exist = repository.findOne(nodeId);
        if (exist != null) {
            Node node = new Node();
            BeanUtils.copyProperties(exist, node, "id", "createAt", "updateAt");
            node.setName(name);
            MessageCollectItem messageCollectItem = exist.getMessageCollectItem();
            if (messageCollectItem != null) {
                MessageCollectItem newOne = new MessageCollectItem();
                BeanUtils.copyProperties(messageCollectItem, newOne, "id", "createAt", "updateAt");
                node.setMessageCollectItem(newOne);
            }

            FtpCollectItem ftpCollectItem = exist.getFtpCollectItem();
            if (ftpCollectItem != null) {
                FtpCollectItem newOne = new FtpCollectItem();
                BeanUtils.copyProperties(ftpCollectItem, newOne, "id", "createAt", "updateAt");
                node.setFtpCollectItem(newOne);
            }

            ApiCollectItem apiCollectItem = exist.getApiCollectItem();
            if (apiCollectItem != null) {
                ApiCollectItem newOne = new ApiCollectItem();
                BeanUtils.copyProperties(apiCollectItem, newOne, "id", "createAt", "updateAt");
                node.setApiCollectItem(newOne);
            }

            List<BizItem> bizItems = exist.getBizItems();
            if (bizItems != null) {
                List<BizItem> bizItemList = bizItems.stream()
                        .map(bizItem -> {
                            BizItem obj = new BizItem();
                            BeanUtils.copyProperties(bizItem, obj, "id", "createAt", "updateAt");
                            return obj;
                        })
                        .collect(Collectors.toList());
                node.setBizItems(bizItemList);
            }

            return save(node);
        }
        return false;
    }


    public boolean saveCollectWay(Node nodedto, Long nodeId) {
        try {
            //先删除
            deleteCollectWay(nodedto);
            //再保存
            String way = nodedto.getCollectWay();
            CollectWay collectWay = CollectWay.getCollectWay(way);
            switch (collectWay) {
                case message:
                    //
                    MessageCollectItem messageCollectItem = nodedto.getMessageCollectItem();
                    messageCollectItem.setNodeId(nodeId);
                    return messageCollectItemRepository.insert(messageCollectItem);
                case ftp:
                    //
                    FtpCollectItem ftpCollectItem = nodedto.getFtpCollectItem();
                    ftpCollectItem.setNodeId(nodeId);
                    return ftpCollectItemRepository.insert(ftpCollectItem);
                case api:
                    //
                    ApiCollectItem apiCollectItem = nodedto.getApiCollectItem();
                    apiCollectItem.setNodeId(nodeId);
                    return apiCollectItemRepository.insert(apiCollectItem);
                default:
                    return false;
            }
        } catch (Exception e) {
            log.error("insert collect way failed, reason: {}", e.getLocalizedMessage());
            return false;
        }

    }

    public boolean saveBizItem(Node nodeDto, Long nodeId) {
        //先删除
        deleteBizItem(nodeId);

        //再保存
        List<BizItem> bizItems = nodeDto.getBizItems();
        if (bizItems != null) {
            boolean flag = true;
            for (BizItem bizItem : bizItems) {
                bizItem.setNodeId(nodeId);
                try {
                    boolean succeed = bizItemRepository.insert(bizItem);
                    if (!succeed) {
                        flag = false;
                    }
                } catch (Exception e) {
                    log.error(" insert bizItem failed, reason: {}", e.getLocalizedMessage());
                    flag = false;
                }

            }
            return flag;
        }
        return false;
    }

    public void deleteCollectWay(Node node) {
        String way = node.getCollectWay();
        CollectWay collectWay = CollectWay.getCollectWay(way);
        switch (collectWay) {
            case message:
                List<MessageCollectItem> messageCollectItems = messageCollectItemRepository.findByField("node_id", node.getId());
                if (messageCollectItems != null) {
                    messageCollectItems.forEach(messageCollectItem -> messageCollectItemRepository.delete(messageCollectItem.getId()));
                }
                break;
            case ftp:
                List<FtpCollectItem> ftpCollectItems = ftpCollectItemRepository.findByField("node_id", node.getId());
                if (ftpCollectItems != null) {
                    ftpCollectItems.forEach(ftpCollectItem -> ftpCollectItemRepository.delete(ftpCollectItem.getId()));
                }
                break;
            case api:
                List<ApiCollectItem> apiCollectItems = apiCollectItemRepository.findByField("node_id", node.getId());
                if (apiCollectItems != null) {
                    apiCollectItems.forEach(apiCollectItem -> apiCollectItemRepository.delete(apiCollectItem.getId()));
                }
                break;
            default:
                break;
        }
    }

    public void deleteBizItem(Long nodeId) {
        List<BizItem> bizItems = bizItemRepository.findByField("node_id", nodeId);
        if (bizItems != null) {
            bizItems.forEach(bizItem -> bizItemRepository.delete(bizItem.getId()));

        }
    }

    public void saveCollectWay(Node node, String[] strings) {
        try {
            //先删除 再创建
            deleteCollectWay(node);

            CollectWay collectWay = CollectWay.getCollectWay(node.getCollectWay());
            int collectStartIndex = START_COL_INDEX + 3;
            switch (collectWay) {
                case message:
                    MessageCollectItem messageCollectItem = new MessageCollectItem().setNodeId(node.getId())
                            .setServer(strings[collectStartIndex]).setTopic(strings[collectStartIndex + 1])
                            .setSource(strings[collectStartIndex + 2]).setSourceIp(strings[collectStartIndex + 3]).setType(strings[collectStartIndex + 4]);
                    messageCollectItemRepository.insert(messageCollectItem);
                    break;
                case ftp:
                    //TODO
                    FtpCollectItem ftpCollectItem = new FtpCollectItem().setNodeId(node.getId());
                    ftpCollectItemRepository.insert(ftpCollectItem);
                    break;
                case api:
                    //TODO
                    ApiCollectItem apiCollectItem = new ApiCollectItem().setNodeId(node.getId());
                    apiCollectItemRepository.insert(apiCollectItem);
                    break;
                default:
                    break;
            }
        } catch (Exception e) {
            log.error("insert collect way failed, reason: {}", e.getLocalizedMessage());
        }

    }

    private void saveBizItems(Long nodeId, int length, List<String[]> value) {
        //删除节点的所有配置
        deleteBizItem(nodeId);
        //再创建
        List<BizItem> bizItems = new ArrayList<>();
        for (String[] strings : value) {
            BizItem bizItem = new BizItem()
                    .setNodeId(nodeId)
                    .setStartForecastTime(strings[length - 15])
                    .setStartAge(strings[length - 14])
                    .setInterval((int) Double.parseDouble(strings[length - 13]))
                    .setValidTime((int) Double.parseDouble(strings[length - 12]))
                    .setRemindAt(DateTimeUtil.toDateTime(strings[length - 11]))
                    .setAlarmAt(DateTimeUtil.toDateTime(strings[length - 10]))
                    .setEndAt(DateTimeUtil.toDateTime(strings[length - 9]))
                    .setStartOffset((int) Double.parseDouble(strings[length - 8]))
                    .setFirstLevelAlarmThreshold((int) Double.parseDouble(strings[length - 7]))
                    .setSecondLevelAlarmThreshold((int) Double.parseDouble(strings[length - 6]))
                    .setThirdLevelAlarmThreshold((int) Double.parseDouble(strings[length - 5]))
                    .setFileName(strings[length - 4])
                    .setFileSize(strings[length - 3])
                    .setFileCount((int) Double.parseDouble(strings[length - 2]))
                    .setDataSource(strings[length - 1]);
            bizItems.add(bizItem);
        }
        if (bizItems.size() > 0) {
            bizItemRepository.insert(bizItems);
        }
    }
}
